package 设计模式.创建型模式;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import lombok.Data;

/*
定义：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。

类型：对象创建型模式

类图：
                    聚合                          实现                            依赖
<导演类> Director ◇─────────> <抽象建造者> Builder  ◁------ <建造者> ConcreteBuilder ------> <产品类> Product
                             +setPart
                             +getProduct

•Builder：生成器接口，定义创建一个Product对象所需要的各个部件的操作。
•ConcreteBuilder：具体的生成器实现，实现各个部件的创建，并负责组装Product对象的各个部件，同时还提供一个让用户获取组装完成后的产品对象的方法。
•Director：指导者，也被称导向者，主要用来使用Builder接口，以一个统一的过程来构建所需要的Product对象。
•Product：产品，表示被生成器构建的复杂对象，包含多个部件。

生成器模式示例代码

1、生成器接口定义的示例代码
*/
//生成器接口，定义创建一个产品对象所需的各个部件的操作
/*
interface Builder {
	//示意方法，构建某个部件
	public void buildPart();
}
*/
//2、具体生成器实现的示例代码

//具体的生成器实现对象
/*
class ConcreteBuilder implements Builder {//【实现】------▷  Realization  与Builder是实现关系
	
	private Product resultProduct;//ConcreteBuilder使用(use a)Product
	
	//获取生成器最终构建的产品对象
	public Product getResultProduct() {
		return resultProduct;
	}
 
	@Override
	public void buildPart() {
		//构建某个部件的功能处理
	}
}
*/
//3、相应的产品对象接口的示例代码

//被构建的产品对象的接口
/*
interface Product {//【依赖】------>    Dependency     use a    与ConcreteBuilder是依赖关系  即ConcreteBuilder使用Product
	//定义产品的操作
}
*/
//4、最后是指导者的实现示意，示例代码如下：

//指导者，指导使用生成器的接口来构建产品对象  【聚合】◇ ══════>   Aggregation   has a     即Director拥有Builder
/*
class Director {
 
	//持有当前需要使用的生成器对象
	private Builder builder;
 
	//构造方法，传入生成器对象
	public Director(Builder builder) {
		this.builder = builder;
	}
	
	//示意方法，指导生成器构建最终的产品对象
	public void construct(){
		//通过使用生成器接口来构建最终的产品对象
		builder.buildPart();
	}
}
*/
/*
【应用场景-- 导出数据的应用框架】

在讨论工厂方法模式的时候，提供了一个导出数据的应用框架。

对于导出数据的应用框架，通常在导出数据上，会有一些约束的方式，比如导出成文本格式、数据库备份形式、Excel格式、Xml格式等。

在工厂方法模式章节里面，讨论并使用工厂方法模式来解决了如何选择具体导出方式的问题，并没有涉及到每种方式具体如何实现。

换句话说，在讨论工厂方法模式的时候，并没有讨论如何实现导出成文本、Xml等具体格式，本章就来讨论这个问题。

对于导出数据的应用框架，通常对于具体的导出内容和格式是有要求的，加入现在有如下要求，简单描述一下：

•导出的文件，不管是什么格式，都分成3个部分，分别是文件头、文件体、文件尾。
•在文件头部分，需要描述如下信息：分公司或者门市编号、导出数据的日期。
•在文件体部分，需要描述如下信息：表名称，然后分条描述数据。
•在文件尾部分，需要描述如下信息：输出人。


1、下面将描述文件各个部分的数据对象定义出来  相当于产品某个部件

描述输出到文件头的内容的对象，示例代码如下：
*/
/*
//描述输出到文件头的内容的对象
@Data
class ExportHeaderModel {
	//分公司或者门市编号
	private String depId;
	//导出数据的日期
	private String exportDate;	
}

//描述输出数据的对象，示例代码如下：

//描述输出数据的对象
@Data
class ExportDataModel {
	//产品编号
	private String productId;
	//销售价格
	private double price;
	//销售数量
	private double amount;
}

//描述输出到文件尾的内容的对象，示例代码如下：

//描述输出到文件尾的内容的对象
@Data
class ExportFooterModel {
	//输出人
	private String exportUser; 
}

//2、定义Builder接口，主要是把导出各种格式文件的处理过程的步骤定义出来，每个步骤负责构建最终导出文件的一部分。示例代码如下：
//生成器接口，定义创建一个输出文件对象所需的各个部件的操作
interface Builder {
 
	//构建输出文件的Header部分
	public void buildHeader(ExportHeaderModel ehm);
	
	//构建输出文件的Body部分
	public void buildBody(Map<String,List<ExportDataModel>> mapData);//表名作为xml元素的属性
	
	//构建输出文件的Footer部分
	public void buildFooter(ExportFooterModel efm);
}


//3、具体的生成器实现。
//导出到文本文件的的生成器实现。示例代码如下：

//实现导出文件到文本文件的生成器对象
class TxtBuilder implements Builder {
 
	//用来记录构建的文件的内容，相当于产品
	private StringBuffer buffer = new StringBuffer();
	
	@Override
	public void buildHeader(ExportHeaderModel ehm) {
		buffer.append(ehm.getDepId()+","+ehm.getExportDate()+"\n");
	}
 
	@Override
	public void buildBody(Map<String, List<ExportDataModel>> mapData) {
		//对Map来说，要遍历key可以使用for each循环遍历Map实例的keySet()方法返回的Set集合，它包含不重复的key的集合
		for(String tablName : mapData.keySet()){
			
			//先拼接表名
			buffer.append(tablName+"\n");
			//然后循环拼接具体数据
			for(ExportDataModel edm : mapData.get(tablName)){
				buffer.append(edm.getProductId()+","+edm.getPrice()+","+edm.getAmount()+"\n");
			}
		}
	}
 
	@Override
	public void buildFooter(ExportFooterModel efm) {
		buffer.append(efm.getExportUser());
	}
	
	public StringBuffer getResult(){
		return buffer;
	}
 
}


//导出到Xml文件的的生成器实现。示例代码如下：

//实现导出文件到Xml文件的生成器对象
class XmlBuilder implements Builder {
 
	//用来记录构建的文件的内容，相当于产品
	private StringBuffer buffer = new StringBuffer();
	
	@Override
	public void buildHeader(ExportHeaderModel ehm) {
		buffer.append("<?xml version='1.0' encoding='UTF-8'?>\n");
		buffer.append("<Report>\n");
		buffer.append("\t<Header>\n");
		buffer.append("\t\t<DepId>"+ehm.getDepId()+"</DepId>\n");
		buffer.append("\t\t<ExportDate>"+ehm.getExportDate()+"</ExportDate>\n");
		
		buffer.append("\t</Header>\n");
	}
 
	@Override
	public void buildBody(Map<String, List<ExportDataModel>> mapData) {
		buffer.append("\t<Body>\n");
		for(String tablName : mapData.keySet()){
			//先拼接表名
			buffer.append("\t\t<Datas TableName=\""+tablName+"\">\n");
			//然后循环拼接具体数据
			for(ExportDataModel edm : mapData.get(tablName)){
				
				buffer.append("\t\t\t<Data>\n");
				
				buffer.append("\t\t\t\t<ProductId>"+edm.getProductId()+"</ProductId>\n");
				buffer.append("\t\t\t\t<Price>"+edm.getPrice()+"</Price>\n");
				buffer.append("\t\t\t\t<Amount>"+edm.getAmount()+"</Amount>\n");
				
				buffer.append("\t\t\t</Data>\n");
			}			
			buffer.append("\t\t</Datas>\n");
		}
		buffer.append("\t</Body>\n");
	}
	@Override
	public void buildFooter(ExportFooterModel efm) {
		buffer.append("\t<Footer>\n");
		buffer.append("\t\t<ExportUser>"+efm.getExportUser()+"</ExportUser>\n");
		buffer.append("\t</Footer>\n");
		buffer.append("</Report>\n");
	}	
	public StringBuffer getResult(){
		return buffer;
	}
}


//4、指导者。有了具体的生成器实现后，需要由指导者来指导它进行具体的产品构建。示例代码如下：

//指导者，指导使用生成器的接口来构建输出的文件对象
class Director { 
	//持有当前需要的使用的生成器对象
	private Builder builder; 
	//构造方法，传入生成器对象
	public Director(Builder builder) {
		this.builder = builder;
	}
	public void construct(ExportHeaderModel ehm,Map<String, List<ExportDataModel>> mapData, ExportFooterModel efm) { 
		//1.先构建Header
		builder.buildHeader(ehm);		
		//2.然后构建Body
		builder.buildBody(mapData);		
		//3.再构建Footer
		builder.buildFooter(efm);
	}
}


//5、客户端测试代码如下：

public class 生成器 {
 
	public static void main(String[] args) {
		
		//准备测试数据
		ExportHeaderModel ehm = new ExportHeaderModel();
		ehm.setDepId("一分公司");
		ehm.setExportDate("2010-05-18");
		
		Map<String, List<ExportDataModel>> mapData = new HashMap<String, List<ExportDataModel>>();
		List<ExportDataModel> col = new ArrayList<ExportDataModel>();
		List<ExportDataModel> col1 = new ArrayList<ExportDataModel>();//销售记录表1
		
		ExportDataModel edm1 = new ExportDataModel();
		edm1.setProductId("产品001号");
		edm1.setPrice(100);
		edm1.setAmount(80);
		
		ExportDataModel edm2 = new ExportDataModel();
		edm2.setProductId("产品002号");
		edm2.setPrice(120);
		edm2.setAmount(280);
		
		ExportDataModel edm3 = new ExportDataModel();
		edm3.setProductId("产品003号");
		edm3.setPrice(320);
		edm3.setAmount(380);
		
		col.add(edm1);
		col.add(edm2);
		col.add(edm3);
		
		mapData.put("销售记录表", col);
		
		//---销售记录表1
		ExportDataModel edm11 = new ExportDataModel();
		edm11.setProductId("产品004号");
		edm11.setPrice(100);
		edm11.setAmount(80);
		
		ExportDataModel edm22 = new ExportDataModel();
		edm22.setProductId("产品005号");
		edm22.setPrice(120);
		edm22.setAmount(280);
		
		ExportDataModel edm33 = new ExportDataModel();
		edm33.setProductId("产品006号");
		edm33.setPrice(320);
		edm33.setAmount(380);
		
		col1.add(edm11);
		col1.add(edm22);
		col1.add(edm33);
		
		mapData.put("销售记录表1", col1);
		
		
		ExportFooterModel efm = new ExportFooterModel();
		efm.setExportUser("张三");
		
		//测试输出到文本文件
		TxtBuilder txtBuilder = new TxtBuilder();
		//创建指导者对象
		Director director = new Director(txtBuilder);
		director.construct(ehm, mapData, efm);
		
		//把要输出的内容输出到控制台看看
		System.out.println("输出到文本文件的内容:"+txtBuilder.getResult().toString());
		
		XmlBuilder xmlBuilder = new XmlBuilder();
		Director director2 = new Director(xmlBuilder);
		director2.construct(ehm, mapData, efm);
		
		//把要输出的内容输出到控制台看看
		System.out.println("输出到Xml文件的内容:"+xmlBuilder.getResult().toString());
	}
 
}
*/
/*
【生成器模式的功能】

生成器模式的主要功能是构建复杂的产品，而且是细化的，分步骤的构建产品，也就是生成器模式重在一步一步解决构造复杂对象的问题。如果仅仅这么认知生成器模式的功能是不够的。

更为重要的是，这个构建的过程是统一的、固定不变的，变化的部分放到生成器部分了，只要配置不同的生成器，那么同样的构建过程，就能构建出不同的产品来。

【使用生成器模式构建复杂的对象】

考虑这样的一个实际应用，Android图片异步加载框架，需要要创建图片加载配置的对象，里面很多属性的值都有约束，要求创建出来的对象是满足这些约束规则的。约束规则
比如，线程池的数量不能小于2个、内存图片缓存的大小不能为负值等等。


要想简洁直观、安全性好，有具有很好的扩展性地创建这个对象的话，一个较好的选择就是使用Builder模式，把复杂的创建过程通过Builder来实现。

采用Builder模式来构建复杂的对象，通常会对Builder模式进行一定的简化，因为目标明确，就是创建某个复杂对象，因此做适当简化会使程序更简洁。大致简化如下：

•由于是用Builder模式来创建某个对象，因此就没有必要再定义一个Builder接口，直接提供一个具体的构建器类就可以了。
•对于创建一个负责的对象，可能会有很多种不同的选择和步骤，干脆去掉“指导者”，把指导者的功能和Client的功能合并起来，也就是说，Client就相当于指导者，它来指导构
 建器类去构建需要的复杂对象。
*/

final class ImageLoaderConfiguration {
 
	final Executor taskExecutor;
 
	final int memoryCacheSize;
	
	final int threadPoolSize;
	final int threadPriority;
 
	final boolean writeLogs;
 
	private ImageLoaderConfiguration(final Builder builder) {
		taskExecutor = builder.taskExecutor;
		threadPoolSize = builder.threadPoolSize;
		threadPriority = builder.threadPriority;
		memoryCacheSize = builder.memoryCacheSize;
		writeLogs = builder.writeLogs;
	}
 
	//Builder for {@link ImageLoaderConfiguration}
	public static class Builder {
 
		public static final int DEFAULT_THREAD_POOL_SIZE = 3;
		
		public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 1;
 
		private int memoryCacheSize = 0;
		
		private Executor taskExecutor = null;
 
		private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
		private int threadPriority = DEFAULT_THREAD_PRIORITY;
 
		private boolean writeLogs = false;
 
		public Builder() {
		}
 
		public Builder taskExecutor(Executor executor) {
			if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY) {
			
			}
 
			this.taskExecutor = executor;
			return this;
		}
 
		public Builder threadPoolSize(int threadPoolSize) {
 
			this.threadPoolSize = threadPoolSize;
			return this;
		}
 
		public Builder threadPriority(int threadPriority) {
 
			if (threadPriority < Thread.MIN_PRIORITY) {
				this.threadPriority = Thread.MIN_PRIORITY;
			} else {
				if (threadPriority > Thread.MAX_PRIORITY) {
					this.threadPriority = Thread.MAX_PRIORITY;
				} else {
					this.threadPriority = threadPriority;
				}
			}
			return this;
		}
 
		public Builder memoryCacheSize(int memoryCacheSize) {
			if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");
 
			this.memoryCacheSize = memoryCacheSize;
			return this;
		}
 
		public Builder writeDebugLogs() {
			this.writeLogs = true;
			return this;
		}
 
		//Builds configured {@link ImageLoaderConfiguration} object
		public ImageLoaderConfiguration build() {
			initEmptyFieldsWithDefaultValues();
			return new ImageLoaderConfiguration(this);
		}
 
		private void initEmptyFieldsWithDefaultValues() {
			if (taskExecutor == null) {
				
			}
		}
	}
}

//客户端调用示例代码如下：
public class 生成器 {
 
	public static void main(String[] args) {
		
		ImageLoaderConfiguration  config = new ImageLoaderConfiguration.Builder()
		.taskExecutor(Executors.newCachedThreadPool())
		.threadPoolSize(3)
		.threadPriority(Thread.MIN_PRIORITY + 3)
		.memoryCacheSize(1024*16)
		.build();
	}
 
}

/*
生成器模式的优点

松散耦合

生成器模式可以用同一个构建算法构建出表现上完全不同的产品，实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来，使它和具体产品的表现分松散耦合，从而使得构建算法可以复用，而具体产品表现也可以很灵活地、方便地扩展和切换。

可以很容易的改变产品的内部表示

在生成器模式中，由于Builder对象只是提供接口给Director使用，那么具体部件创建和装配方式是被Builder接口隐藏了的，Director并不知道这些具体的实现细节。这样一来，要想改变产品的内部表示，只需要切换Builder接口的具体实现即可，不用管Director，因此变得很容易。

更好的复用性

生成器模式很好的实现构建算法和具体产品实现的分离。这样一来，使得构建产品的算法可以复用。同样的道理，具体产品的实现也可以复用，同一个产品的实现，可以配合不同的构建算法使用。


生成器模式的本质：分离整体构建算法和部件构造。

虽然在生成器模式的整体构建算法中，会一步一步引导Builder来构建对象，但这并不是说生成器主要就是用来实现分步骤构建对象的。生成器模式的重心还是在于分离整体构建算法和部件构造，而分步骤构建对象不过是整体构建算法的一个简单表现，或者说是一个附带产物。


何时选用生成器模式

建议在以下情况中选用生成器模式。

如果创建对象的算法，应该独立于该对象的组成部分以及它们的装配方式时。
如果同一个构建过程有着不同的表示时。
*/
